package repository

import (
	"context"
	"database/sql"
	"fmt"
	"strings"

	"mall/common/modelx"
	"mall/service/blog/model"

	"github.com/volatiletech/sqlboiler/v4/boil"
	"github.com/volatiletech/sqlboiler/v4/queries/qm"
	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	// BlogUserRepository is an interface to be customized, add more methods here,
	// and implement the added methods in customBlogUserRepository.
	BlogUserRepository interface {
		Conn() error
		GetClickLogs(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.BlogUserClickLog, int64, error)
		GetClickObjectList(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.BlogUserClickLog, int64, error) //////
		GetSearchLogs(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.BlogUserSearchLog, int64, error)
		GetProposals(
			ctx context.Context,
			cols model.M,
			withCount int,
			filter map[string]map[string][]any,
		) ([]*model.BlogKnowledgeBaseProposal, int64, error)
		InsertClickLog(
			ctx context.Context,
			log *model.BlogUserClickLog,
		) error
		InsertSearchLog(
			ctx context.Context,
			log *model.BlogUserSearchLog,
		) int64  //////
		InsertProposal(
			ctx context.Context,
			log *model.BlogKnowledgeBaseProposal,
		) error  ////
		UpdateSearchLogBatch(ctx context.Context, ids []int64, cols model.M) (int64, error)
		ForceDeleteSearchLogBatch(ctx context.Context, ids []int64) (int64, error)
	}

	customBlogUserRepository struct {
		cacheConf  cache.CacheConf
		dataSource string

		mysqlConn *sql.DB
	}
)

// NewBlogUserRepository returns a repository for the database table.
func NewBlogUserRepository(dataSource string, cacheConf cache.CacheConf) BlogUserRepository {
	return &customBlogUserRepository{
		cacheConf:  cacheConf,
		dataSource: dataSource,
	}
}

func (r *customBlogUserRepository) Conn() error {
	if r.mysqlConn == nil {
		mysqlConn, err := modelx.NewMysqlConn(r.dataSource)
		if err != nil {
			return err
		}

		r.mysqlConn = mysqlConn
	}

	return nil
}

func (r *customBlogUserRepository) GetClickLogs(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.BlogUserClickLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.BlogUserClickLogColumns.ClickLogID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ClickLogID, opMap)...)
			case model.BlogUserClickLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.UserID, opMap)...)
			case model.BlogUserClickLogColumns.PlatformID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.PlatformID, opMap)...)
			case model.BlogUserClickLogColumns.SearchLogID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.SearchLogID, opMap)...)
			case model.BlogUserClickLogColumns.ObjectID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ObjectID, opMap)...)
			case model.BlogUserClickLogColumns.ObjectType:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ObjectType, opMap)...)
			case model.BlogUserClickLogColumns.SectionID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.SectionID, opMap)...)
			case model.BlogUserClickLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.Status, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods) + 3)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))
			subMods = append(subMods, qm.Select(model.BlogUserClickLogColumns.ClickLogID))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset - 1))
				previousList, previousErr := model.BlogUserClickLogs(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}
				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset + limit))
				nextList, nextErr := model.BlogUserClickLogs(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}
				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount * 2
		} else {
			count, err = model.BlogUserClickLogs(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.BlogUserClickLogColumns.ClickLogID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.BlogUserClickLogs(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customBlogUserRepository) GetClickObjectList(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.BlogUserClickLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.BlogUserClickLogColumns.ClickLogID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ClickLogID, opMap)...)
			case model.BlogUserClickLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.UserID, opMap)...)
			case model.BlogUserClickLogColumns.PlatformID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.PlatformID, opMap)...)
			case model.BlogUserClickLogColumns.SearchLogID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.SearchLogID, opMap)...)
			case model.BlogUserClickLogColumns.ObjectID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ObjectID, opMap)...)
			case model.BlogUserClickLogColumns.ObjectType:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.ObjectType, opMap)...)
			case model.BlogUserClickLogColumns.SectionID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.SectionID, opMap)...)
			case model.BlogUserClickLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.BlogUserClickLogColumns.Status, opMap)...)
			}
		}
	}

	mods = append(mods, qm.Select(fmt.Sprintf(
		"max(%s) as %s, %s, max(%s) as %s",
		model.BlogUserClickLogColumns.ClickLogID,
		model.BlogUserClickLogColumns.ClickLogID,
		model.BlogUserClickLogColumns.ObjectID,
		model.BlogUserClickLogColumns.SectionID,
		model.BlogUserClickLogColumns.SectionID,
	)))
	mods = append(mods, qm.GroupBy(model.BlogUserClickLogColumns.ObjectID))

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods) + 2)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset - 1))
				previousList, previousErr := model.BlogUserClickLogs(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}

				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset + limit))
				nextList, nextErr := model.BlogUserClickLogs(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}

				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount * 2
		} else {
			count, err = model.BlogUserClickLogs(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	list, err = model.BlogUserClickLogs(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customBlogUserRepository) GetSearchLogs(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.BlogUserSearchLog, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var offset int
	var limit int
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "offset":
			if val, ok := v.(int32); ok && val > 0 {
				offset = int(val)
			}
		case "limit":
			if val, ok := v.(int32); ok && val > 0 {
				limit = int(val)
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.BlogUserSearchLogColumns.SearchLogID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserSearchLogColumns.SearchLogID, opMap)...)
			case model.BlogUserSearchLogColumns.UserID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserSearchLogColumns.UserID, opMap)...)
			case model.BlogUserSearchLogColumns.PlatformID:
				mods = append(mods, modelx.GetIntMods(model.BlogUserSearchLogColumns.PlatformID, opMap)...)
			case model.BlogUserSearchLogColumns.Status:
				mods = append(mods, modelx.GetIntMods(model.BlogUserSearchLogColumns.Status, opMap)...)
			}
		}
	}

	mods = append(mods, qm.Select(fmt.Sprintf(
		"max(%s) as %s, %s, count(%s) as %s",
		model.BlogUserSearchLogColumns.SearchLogID,
		model.BlogUserSearchLogColumns.SearchLogID,
		model.BlogUserSearchLogColumns.Keyword,
		model.BlogUserSearchLogColumns.SearchLogID,
		model.BlogUserSearchLogColumns.TotalCount,
	)))
	mods = append(mods, qm.GroupBy(model.BlogUserSearchLogColumns.Keyword))
	mods = append(mods, qm.OrderBy(model.BlogUserSearchLogColumns.TotalCount + " DESC"))

	if withCount >= 0 {
		if withCount >= 2 {
			var nextCount int64
			var previousCount int64
			subMods := make([]qm.QueryMod, len(mods), len(mods) + 2)
			copy(subMods, mods)
			subMods = append(subMods, qm.Limit(1))

			if withCount != 3 && offset >= 1 {
				subMods = append(subMods, qm.Offset(offset - 1))
				previousList, previousErr := model.BlogUserSearchLogs(subMods...).All(ctx, r.mysqlConn)
				if previousErr != nil {
					err = previousErr
					return
				}

				if len(previousList) > 0 {
					previousCount = 1
				}
			}
			if limit > 0 {
				subMods = append(subMods, qm.Offset(offset + limit))
				nextList, nextErr := model.BlogUserSearchLogs(subMods...).All(ctx, r.mysqlConn)
				if nextErr != nil {
					err = nextErr
					return
				}

				if len(nextList) > 0 {
					nextCount = 1
				}
			}

			count = previousCount + nextCount * 2
		} else {
			count, err = model.BlogUserSearchLogs(mods...).Count(ctx, r.mysqlConn)
			if err != nil {
				return
			}

			if withCount == 0 {
				return
			}
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	} else if limit > 0 || offset > 0 {
		if limit > 0 {
			mods = append(mods, qm.Limit(limit))
		}
		if offset > 0 {
			mods = append(mods, qm.Offset(offset))
		}
	}

	list, err = model.BlogUserSearchLogs(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customBlogUserRepository) GetProposals(
	ctx context.Context,
	cols model.M,
	withCount int,
	filter map[string]map[string][]any,
) (list []*model.BlogKnowledgeBaseProposal, count int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	var page int
	var pageSize int
	var selectCol string
	for k, v := range cols {
		switch k {
		case "page":
			if val, ok := v.(int64); ok && val > 1 {
				page = int(val)
			}
		case "pageSize":
			if val, ok := v.(int64); ok && val > 0 {
				pageSize = int(val)
			}
		case "orderBy":
			if val, ok := v.(string); ok && val != "" {
				mods = append(mods, qm.OrderBy(val))
			}
		case "select":
			if val, ok := v.(string); ok && val != "" {
				selectCol = val
			}
		}
	}

	if filter != nil {
		for key, opMap := range filter {
			switch key {
			case model.BlogKnowledgeBaseProposalColumns.ID:
				mods = append(mods, modelx.GetIntMods(model.BlogKnowledgeBaseProposalColumns.ID, opMap)...)
			case model.BlogKnowledgeBaseProposalColumns.PostAuthorID:
				mods = append(mods, modelx.GetIntMods(model.BlogKnowledgeBaseProposalColumns.PostAuthorID, opMap)...)
			case model.BlogKnowledgeBaseProposalColumns.PostParentID:
				mods = append(mods, modelx.GetIntMods(model.BlogKnowledgeBaseProposalColumns.PostParentID, opMap)...)
			case model.BlogKnowledgeBaseProposalColumns.PostStatus:
				mods = append(mods, modelx.GetIntMods(model.BlogKnowledgeBaseProposalColumns.PostStatus, opMap)...)
			}
		}
	}

	if withCount >= 0 {
		count, err = model.BlogKnowledgeBaseProposals(mods...).Count(ctx, r.mysqlConn)
		if err != nil {
			return
		}

		if withCount == 0 {
			return
		}
	}

	if pageSize > 0 {
		if page > 1 {
			mods = append(mods, qm.Offset(pageSize*(page-1)))
		}
		mods = append(mods, qm.Limit(pageSize))
	}

	var columns []string
	if selectCol != "" {
		if selectCol != "," {
			columns = strings.Split(selectCol, ",")
		}
		columns = append(columns, model.BlogKnowledgeBaseProposalColumns.ID)
	}
	if len(columns) > 0 {
		mods = append(mods, qm.Select(columns...))
	}

	list, err = model.BlogKnowledgeBaseProposals(mods...).All(ctx, r.mysqlConn)
	return
}

func (r *customBlogUserRepository) InsertClickLog(
	ctx context.Context,
	log *model.BlogUserClickLog,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return log.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customBlogUserRepository) InsertSearchLog(
	ctx context.Context,
	log *model.BlogUserSearchLog,
) int64 {
	if err := r.Conn(); err != nil {
		return 0
	}

	var searchLogID int64
	keywords := strings.Split(log.Keyword, ",")
	if len(keywords) > 0 {
		for _, keyword := range keywords {
			log.Keyword = keyword
			err := log.Insert(ctx, r.mysqlConn, boil.Infer())
			if searchLogID == 0 && err == nil {
				searchLogID = int64(log.SearchLogID) 
			}
		}
	}

	return searchLogID
}

func (r *customBlogUserRepository) InsertProposal(
	ctx context.Context,
	log *model.BlogKnowledgeBaseProposal,
) error {
	if err := r.Conn(); err != nil {
		return err
	}

	return log.Insert(ctx, r.mysqlConn, boil.Infer())
}

func (r *customBlogUserRepository) UpdateSearchLogBatch(ctx context.Context, ids []int64, cols model.M) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]any, 0, len(ids))
		for _, id := range ids {
			values = append(values, uint64(id))
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BlogUserSearchLogColumns.SearchLogID), values...))
	}

	rowsAff, err = model.BlogUserSearchLogs(mods...).UpdateAll(ctx, r.mysqlConn, cols)
	return
}

func (r *customBlogUserRepository) ForceDeleteSearchLogBatch(ctx context.Context, ids []int64) (rowsAff int64, err error) {
	if err = r.Conn(); err != nil {
		return
	}

	var mods []qm.QueryMod
	if len(ids) > 0 {
		values := make([]any, 0, len(ids))
		for _, id := range ids {
			values = append(values, id)
		}

		mods = append(mods, qm.WhereIn(fmt.Sprintf("%s IN ?", model.BlogUserSearchLogColumns.SearchLogID), values...))
	}

	rowsAff, err = model.BlogUserSearchLogs(mods...).DeleteAll(ctx, r.mysqlConn)
	return
}
